package org.linlinjava.litemall.admin.service;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.RequiredArgsConstructor;
import org.linlinjava.litemall.admin.beans.enums.PromptEnum;
import org.linlinjava.litemall.admin.controller.cashsettlement.*;
import org.linlinjava.litemall.admin.dto.AdminCashDetailsDTO;
import org.linlinjava.litemall.admin.dto.AdminCashSettlementDTO;
import org.linlinjava.litemall.admin.errorcode.ErrorCodeConstants;
import org.linlinjava.litemall.admin.vo.cashsettlement.CashTransactionSummaryResp;
import org.linlinjava.litemall.core.exception.util.ServiceExceptionUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.domain.*;
import org.linlinjava.litemall.db.service.*;
import org.linlinjava.litemall.db.util.BeanUtil;
import org.linlinjava.litemall.db.util.CollectionUtils;
import org.linlinjava.litemall.db.util.MapUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static org.linlinjava.litemall.admin.errorcode.ErrorCodeConstants.LUMIERE_AMOUNT_DIFFERENCE_BATCH_UPDATE_FAILED;
import static org.linlinjava.litemall.admin.errorcode.ErrorCodeConstants.LUMIERE_AMOUNT_DIFFERENCE_ID_LIST_EMPTY;

@Service
@RequiredArgsConstructor
public class AdminCashSettlementService {
    private final LitemallCashSettlementService cashSettlementService;
    private final LitemallCashScheduleService litemallCashScheduleService;
    private final LitemallShopRegionService litemallShopRegionService;
    private final LumiereAmountDifferenceService lumiereAmountDifferenceService;
    private final LitemallUserService litemallUserService;
    private final LitemallAdminService litemallAdminService;
    private final LitemallShopService litemallShopService;

    public ResponseUtil.Response summary(AdminCashSettlementDTO cashSettlementDTO) {
        LocalDateTime[] timeHorizon = cashSettlementDTO.getTimeHorizon();

        //区域
        Collection<Integer> shopIds = cashSettlementDTO.getRegionId()==null?
                CollUtil.newArrayList() :CollStreamUtil.toSet(litemallShopRegionService.queryByRegionId(cashSettlementDTO.getRegionId()),LitemallShopRegion::getShopId);

        List<LitemallCashSettlement> cashSettlementList = cashSettlementService.selectJoinList(LitemallCashSettlement.class,new MPJLambdaWrapper<LitemallCashSettlement>()
                .selectAll(LitemallCashSettlement.class)
                .selectAs(LitemallUser::getNickname,LitemallCashSettlement::getUsername)
                .selectAs(LitemallShop::getName,LitemallCashSettlement::getShopName)
                .like(StrUtil.isNotEmpty(cashSettlementDTO.getUsername()), LitemallUser::getNickname,cashSettlementDTO.getUsername())
                .eq(cashSettlementDTO.getShopId()!=null,LitemallCashSettlement::getShopId,cashSettlementDTO.getShopId())
                .in(shopIds.size()>0,LitemallCashSettlement::getShopId,shopIds)
                .eq(cashSettlementDTO.getType()!=null,LitemallCashSettlement::getType,cashSettlementDTO.getType())
                .between(timeHorizon!=null,LitemallCashSettlement::getAddTime,timeHorizon!=null?timeHorizon[0]:null,timeHorizon!=null?timeHorizon[1]:null)
                .leftJoin(LitemallUser.class,LitemallUser::getId,LitemallCashSettlement::getUserId)
                .leftJoin(LitemallShop.class,LitemallShop::getId,LitemallCashSettlement::getShopId)
                .orderByDesc(LitemallCashSettlement::getAddTime)
        );
        //统计
        //总订单金额
        BigDecimal totalOrderAmount = BigDecimal.ZERO;
        //现金充值总额
        //BigDecimal totalCashRechargeAmount = BigDecimal.ZERO;
        //实收总现金
        BigDecimal totalActualReceivedCash = BigDecimal.ZERO;
        //收取总现金
        BigDecimal totalCollectedCash = BigDecimal.ZERO;
        //找回总现金
        BigDecimal totalChangeGivenCash = BigDecimal.ZERO;
        //退款总现金
        BigDecimal totalRefundedCash = BigDecimal.ZERO;
        //相差总现金
        BigDecimal totalCashDifference = BigDecimal.ZERO;
        for (LitemallCashSettlement cashSettlement : cashSettlementList) {
            totalOrderAmount = totalOrderAmount.add(cashSettlement.getOrderCash());
            //totalCashRechargeAmount = totalCashRechargeAmount.add(cashSettlement.getActualCash());
            totalActualReceivedCash = totalActualReceivedCash.add(cashSettlement.getActualCash());
            totalCollectedCash = totalCollectedCash.add(cashSettlement.getReceiveCash());
            totalChangeGivenCash = totalChangeGivenCash.add(cashSettlement.getChange());
            totalRefundedCash = totalRefundedCash.add(cashSettlement.getRefundCash());
            totalCashDifference = totalCashDifference.add(cashSettlement.getDifferenceAmount());
        }
        return ResponseUtil.ok(new CashTransactionSummaryResp()
                .setTotalOrderAmount(totalOrderAmount)
                //.setTotalCashRechargeAmount(totalCashRechargeAmount)
                .setTotalActualReceivedCash(totalActualReceivedCash)
                .setTotalCollectedCash(totalCollectedCash)
                .setTotalChangeGivenCash(totalChangeGivenCash)
                .setTotalRefundedCash(totalRefundedCash)
                .setTotalCashDifference(totalCashDifference)
        );
    }
    public ResponseUtil.Response cashSettlements(AdminCashSettlementDTO cashSettlementDTO) {
        LocalDateTime[] timeHorizon = cashSettlementDTO.getTimeHorizon();

        //区域
        Collection<Integer> shopIds = cashSettlementDTO.getRegionId()==null?
                CollUtil.newArrayList() :CollStreamUtil.toSet(litemallShopRegionService.queryByRegionId(cashSettlementDTO.getRegionId()),LitemallShopRegion::getShopId);
        IPage<LitemallCashSettlement> litemallCashSettlements = cashSettlementService.selectJoinListPage(new Page<>(cashSettlementDTO.getPage(),cashSettlementDTO.getLimit()),LitemallCashSettlement.class,new MPJLambdaWrapper<LitemallCashSettlement>()
                .selectAll(LitemallCashSettlement.class)
                .selectAs(LitemallUser::getNickname,LitemallCashSettlement::getUsername)
                .selectAs(LitemallShop::getName,LitemallCashSettlement::getShopName)
                .like(StrUtil.isNotEmpty(cashSettlementDTO.getUsername()), LitemallUser::getNickname,cashSettlementDTO.getUsername())
                .eq(cashSettlementDTO.getShopId()!=null,LitemallCashSettlement::getShopId,cashSettlementDTO.getShopId())
                .in(shopIds.size()>0,LitemallCashSettlement::getShopId,shopIds)
                .eq(cashSettlementDTO.getType()!=null,LitemallCashSettlement::getType,cashSettlementDTO.getType())
                .between(timeHorizon!=null,LitemallCashSettlement::getAddTime,timeHorizon!=null?timeHorizon[0]:null,timeHorizon!=null?timeHorizon[1]:null)
                .leftJoin(LitemallUser.class,LitemallUser::getId,LitemallCashSettlement::getUserId)
                .leftJoin(LitemallShop.class,LitemallShop::getId,LitemallCashSettlement::getShopId)
                .orderByDesc(LitemallCashSettlement::getAddTime)
        );
        return ResponseUtil.okPage(litemallCashSettlements);
    }

    public ResponseUtil.Response cashDetails(AdminCashDetailsDTO cashDetailsDTO) {
        IPage<LitemallCashSchedule> cashSchedules = litemallCashScheduleService.page(new Page<>(cashDetailsDTO.getPage(),cashDetailsDTO.getLimit()),Wrappers.lambdaQuery(LitemallCashSchedule.class)
                        .eq(LitemallCashSchedule::getUserId,cashDetailsDTO.getUserId())
                        .eq(LitemallCashSchedule::getShopId,cashDetailsDTO.getShopId())
                        .eq(LitemallCashSchedule::getDeviceNo,cashDetailsDTO.getDeviceNo())
                        .between(LitemallCashSchedule::getPayTime,cashDetailsDTO.getLastCheck(),cashDetailsDTO.getAddTime())
                .orderByDesc(LitemallCashSchedule::getPayTime));
        return ResponseUtil.okPage(cashSchedules);
    }


    public Integer amountDifferenceAdd(AmountDifferenceReqVO reqVO) {
        List<LumiereAmountDifference> amountDifferenceList = lumiereAmountDifferenceService.list(Wrappers.lambdaQuery(LumiereAmountDifference.class).eq(LumiereAmountDifference::getShopId, reqVO.getShopId()));
        if(CollUtil.isNotEmpty(amountDifferenceList)){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.LUMIERE_AMOUNT_DIFFERENCE_SHOP_EXIST);
        }
        LumiereAmountDifference difference = BeanUtil.copy(reqVO, LumiereAmountDifference.class);
        lumiereAmountDifferenceService.save(difference);
        return difference.getId();
    }

    public Integer amountDifferenceEdit(AmountDifferenceReqVO reqVO) {
        if(reqVO.getId()==null){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.LUMIERE_AMOUNT_DIFFERENCE_ID_LIST_EMPTY);
        }
        LumiereAmountDifference difference = BeanUtil.copy(reqVO, LumiereAmountDifference.class);
        lumiereAmountDifferenceService.updateById(difference);
        return difference.getId();
    }

    public IPage<AmountDifferenceRespVO> amountDifferencePage(AmountDifferencePageReqVO pageReqVO) {
        LambdaQueryWrapper<LumiereAmountDifference> queryWrapper = new LambdaQueryWrapper<>();

        Optional.ofNullable(pageReqVO.getAdminId())
                .ifPresent(adminId -> queryWrapper.eq(LumiereAmountDifference::getAdminId, adminId));

        Optional.ofNullable(pageReqVO.getEnabled())
                .ifPresent(enabled -> queryWrapper.eq(LumiereAmountDifference::getEnabled, enabled));

        Optional.ofNullable(pageReqVO.getCreateTime())
                .filter(arr -> arr.length == 2 && arr[0] != null && arr[1] != null)
                .ifPresent(range -> queryWrapper.between(LumiereAmountDifference::getAddTime, range[0], range[1]));

        IPage<LumiereAmountDifference> amountDifferencePage = lumiereAmountDifferenceService.page(new Page<>(pageReqVO.getPage(), pageReqVO.getLimit()), queryWrapper);

        Map<Integer, LitemallAdmin> adminMap = litemallAdminService.getAdminMap(CollectionUtils.convertList(amountDifferencePage.getRecords(), LumiereAmountDifference::getAdminId));
        Map<Integer, LitemallShop> shoMap = litemallShopService.getShoMap(CollectionUtils.convertList(amountDifferencePage.getRecords(), LumiereAmountDifference::getShopId));
        IPage<AmountDifferenceRespVO> amountDifferencePageRespVOPage = BeanUtil.copyPage(amountDifferencePage, AmountDifferenceRespVO.class);
        amountDifferencePageRespVOPage.getRecords().forEach(amountDifference->{
            MapUtils.findAndThen(adminMap,amountDifference.getAdminId(),admin->amountDifference.setNickname(admin.getNickName()).setEmail(admin.getEmail()));
            MapUtils.findAndThen(shoMap,amountDifference.getShopId(),shop->amountDifference.setShopName(shop.getName()));

        });
        return amountDifferencePageRespVOPage;
    }

    public Boolean enableAmountDifferenceBatch(AmountDifferenceEnableBatchReqVO reqVO) {
        // 空校验
        if (CollUtil.isEmpty(reqVO.getIds())) {
            throw ServiceExceptionUtil.exception(LUMIERE_AMOUNT_DIFFERENCE_ID_LIST_EMPTY);
        }

        // 批量更新数据库字段
        boolean success = lumiereAmountDifferenceService.update(
                Wrappers.lambdaUpdate(LumiereAmountDifference.class)
                        .in(LumiereAmountDifference::getId, reqVO.getIds())
                        .set(LumiereAmountDifference::getEnabled, reqVO.getEnabled())
                        .set(LumiereAmountDifference::getUpdateTime, LocalDateTime.now())
        );

        if (!success) {
            throw ServiceExceptionUtil.exception(LUMIERE_AMOUNT_DIFFERENCE_BATCH_UPDATE_FAILED);
        }

        return true;
    }


    public Boolean deletedAmountDifferenceBatch(AmountDifferenceDeletedBatchReqVO reqVO) {
        // 空校验
        if (CollUtil.isEmpty(reqVO.getIds())) {
            throw ServiceExceptionUtil.exception(LUMIERE_AMOUNT_DIFFERENCE_ID_LIST_EMPTY);
        }
        lumiereAmountDifferenceService.removeBatchByIds(reqVO.getIds());
        return true;
    }
}
